package cn.clx.ymcc.service.impl;


import cn.clx.ymcc.doc.CourseDoc;
import cn.clx.ymcc.dto.CourseEsDto;
import cn.clx.ymcc.esmapper.HighlightResultMapper;
import cn.clx.ymcc.repository.CourseElasticsearchRepository;
import cn.clx.ymcc.result.PageList;
import cn.clx.ymcc.service.ISearchService;
import cn.clx.ymcc.vo.AggrsBucket;
import cn.clx.ymcc.vo.EsAggsPageList;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class SearchServiceImpl implements ISearchService {

    @Autowired
    private CourseElasticsearchRepository courseElasticsearchRepository;

    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;

    @Autowired
    private HighlightResultMapper highlightResultMapper;

    /**
     * 课程上架到ES
     * @param doc
     */
    @Override
    public void saveES(CourseDoc doc) {
        courseElasticsearchRepository.save(doc);
    }

    /**
     * ES中删除 基于id
     * @param id
     */
    @Override
    public void deleteES(Long id) {
        courseElasticsearchRepository.deleteById(id);
    }

    /**
     *搜索ES数据返还给前端
     * @param dto
     * @return
     */
    @Override
    public PageList<CourseDoc> search(CourseEsDto dto) {
        //通过NativeSearchQueryBuilder这个构建查询
        NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();

        if(StringUtils.hasLength(dto.getSortField())){
            SortOrder sortOrder = dto.getSortType().equalsIgnoreCase("ASC") ? SortOrder.ASC : SortOrder.DESC;
            //排序字段dto.getSortField()，和怎么排序sortOrder
            /**
             * 本来是要new SortBuilder() 它的来穿件排序规则，但是它是一个接口，只能找到它的子类 new 它的子类来创建排序规则
             */
            builder.withSort(new FieldSortBuilder(dto.getSortField()).order(sortOrder));
        }
        //创建分页规则        同样的Pageable是个接口，只能找它的子类PageRequest来生成对象
        PageRequest of = PageRequest.of(dto.getPage() - 1, dto.getRows());
        builder.withPageable(of);
        //创建查询条件DSL查询  通过工具类创建DSL查询条件
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        if(StringUtils.hasLength(dto.getKeyword())){
            boolQuery.must(QueryBuilders.matchQuery("name",dto.getKeyword()));
        }
        //DSL查询----------------
        if(StringUtils.hasLength(dto.getKeyword())){
            boolQuery.must(QueryBuilders.matchQuery("name",dto.getKeyword()));
        }

        //DSL过滤-----------------
        if(dto.getCourseTypeId() != null){
            boolQuery.filter(QueryBuilders.termQuery("courseTypeId",dto.getCourseTypeId()));
        }
        if(StringUtils.hasLength(dto.getGradeName())){
            boolQuery.filter(QueryBuilders.termQuery("gradeName",dto.getGradeName()));
        }
        if(StringUtils.hasLength(dto.getChargeName())){
            boolQuery.filter(QueryBuilders.termQuery("chargeName",dto.getChargeName()));
        }
        if(dto.getPriceMin() != null){
            boolQuery.filter(QueryBuilders.rangeQuery("price").gte(dto.getPriceMin()));
        }
        if(dto.getPriceMax() != null){
            boolQuery.filter(QueryBuilders.rangeQuery("price").lte(dto.getPriceMax()));
        }

        //添加高亮显示  1设置高亮展示样式 2处理映射结果，用映射器包装起来
      /*
        这个方法高亮展示不会分词
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.preTags("<font style='color:red'").postTags("</font>").field("name");
        builder.withHighlightBuilder(highlightBuilder);
        */
        builder.withHighlightFields(
                new HighlightBuilder.Field("name")
                        .preTags("<font style='color:red'>")
                        .postTags("</font>"));

        //添加聚合搜索  aggsGradeName 别名  gradeName 对这个字段进行分组
        builder.addAggregation(AggregationBuilders.terms("aggsGradeName").field("gradeName"));
        builder.addAggregation(AggregationBuilders.terms("aggsChargeName").field("chargeName"));

        //添加DSL查询
        builder.withQuery(boolQuery);
        //搜索ES
        NativeSearchQuery build = builder.build();
        //Page<CourseDoc> search = courseElasticsearchRepository.search(build);
        AggregatedPage<CourseDoc> page = elasticsearchRestTemplate.queryForPage(build, CourseDoc.class, highlightResultMapper);

        Map<String, Aggregation> stringAggregationMap = page.getAggregations().asMap();
        //以gradeName作为分组条件的 List<AggrsBucket> 对象  用的子类ParsedStringTerms来接收
        ParsedStringTerms aggsGradeName = (ParsedStringTerms) stringAggregationMap.get("aggsGradeName");
        List<AggrsBucket> aggrsBucket1 = new ArrayList<>();
        aggsGradeName.getBuckets().forEach(bucket ->{
            AggrsBucket aggrsBucket = new AggrsBucket();
            aggrsBucket.setKey(bucket.getKeyAsString());
            aggrsBucket.setDocCount(bucket.getDocCount());
            aggrsBucket1.add(aggrsBucket);
        });
        //把查询出来的ES数据封装成List<AggrsBucket>
        ParsedStringTerms aggsChargeName = (ParsedStringTerms)stringAggregationMap.get("aggsChargeName");
        List<AggrsBucket> aggrsBucket2 = new ArrayList<>();
        aggsChargeName.getBuckets().forEach(bucket ->{
            AggrsBucket aggrsBucket = new AggrsBucket();
            aggrsBucket.setKey(bucket.getKeyAsString());
            aggrsBucket.setDocCount(bucket.getDocCount());
            aggrsBucket2.add(aggrsBucket);
        });
        //再把数据封装成 HashMap<String, List<AggrsBucket>>
        HashMap<String, List<AggrsBucket>> map = new HashMap<>();
        map.put("aggsGradeName",aggrsBucket1);
        map.put("aggsChargeName",aggrsBucket2);

        // 封装返还给前端的值  search.getTotalElements() 总条数  search.getContent() 数据
        return new EsAggsPageList<CourseDoc>(page.getTotalElements(),page.getContent(),map);
    }
}
